package com.ch.base.handler;

import lombok.Getter;
import org.apache.poi.xssf.model.SharedStringsTable;
import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * process xml handler
 * created in 2019-04-10 18:23
 *
 * @author ray
 * @version 2.0
 **/
@Getter
public class SheetHandler extends DefaultHandler {

    private SharedStringsTable sst;
    private boolean isString;
    private String value;//<v></v>存储标签数据值
    private int rowNum;//行数
    private String reference;//单元格索引
    private List<Map<String, String>> data;//excel数据内容
    private Map<String, String> datum;//每行数据内容

    public SheetHandler(SharedStringsTable sst) {

        this.sst = sst;
        this.isString = false;
        this.rowNum = 0;
        this.data = new ArrayList<>();
    }

    /**
     * Receive notification of the start of an element.
     *
     * <p>By default, do nothing.  Application writers may override this
     * method in a subclass to take specific actions at the start of
     * each element (such as allocating a new tree node or writing
     * output to a file).</p>
     *
     * @param uri        The Namespace URI, or the empty string if the
     *                   element has no Namespace URI or if Namespace
     *                   processing is not being performed.
     * @param localName  The local name (without prefix), or the
     *                   empty string if Namespace processing is not being
     *                   performed.
     * @param qName      The qualified name (with prefix), or the
     *                   empty string if qualified names are not available.
     * @param attributes The attributes attached to the element.  If
     *                   there are no attributes, it shall be an empty
     *                   Attributes object.
     * @throws SAXException Any SAX exception, possibly
     *                      wrapping another exception.
     * @see ContentHandler#startElement
     */
    @Override
    public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {

        if ("row".equals(qName)) {
            this.rowNum++;
            this.datum = new HashMap<>();
        }
        if ("c".equals(qName)) {
            //r为索引:A1/B1/C1;t为类型:string类型 -> t=s;其它为null
            //<c r=A1 s=1 t=s> <c r=B1 s=2>
            String type = attributes.getValue("t");//获取单元格类型
            this.reference = attributes.getValue("r").split("[0-9]+")[0];
            this.isString = "s".equals(type);
        }
        //clear value cache
        this.value = "";
    }

    /**
     * Receive notification of the end of an element.
     *
     * <p>By default, do nothing.  Application writers may override this
     * method in a subclass to take specific actions at the end of
     * each element (such as finalising a tree node or writing
     * output to a file).</p>
     *
     * @param uri       The Namespace URI, or the empty string if the
     *                  element has no Namespace URI or if Namespace
     *                  processing is not being performed.
     * @param localName The local name (without prefix), or the
     *                  empty string if Namespace processing is not being
     *                  performed.
     * @param qName     The qualified name (with prefix), or the
     *                  empty string if qualified names are not available.
     * @throws SAXException Any SAX exception, possibly
     *                      wrapping another exception.
     * @see ContentHandler#endElement
     */
    @Override
    public void endElement(String uri, String localName, String qName) throws SAXException {
        //save cell value
        if ("v".equals(qName)) {
            if (this.isString) {
                this.value = sst.getItemAt(Integer.parseInt(this.value)).getString();
                this.isString = false;
            }
            //put value into datum
            this.datum.put(this.reference, this.value);
        } else if ("row".equals(qName)) {
            //put datum into data
            this.data.add(this.datum);
        }
    }

    /**
     * Receive notification of character data inside an element.
     *
     * <p>By default, do nothing.  Application writers may override this
     * method to take specific actions for each chunk of character data
     * (such as adding the data to a node or buffer, or printing it to
     * a file).</p>
     *
     * @param ch     The characters.
     * @param start  The start position in the character array.
     * @param length The number of characters to use from the
     *               character array.
     * @throws SAXException Any SAX exception, possibly
     *                      wrapping another exception.
     * @see ContentHandler#characters
     */
    @Override
    public void characters(char[] ch, int start, int length) throws SAXException {

        this.value += new String(ch, start, length);
    }
}